package de.rj.urwalking.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

import android.util.Log;
import de.rj.urwalking.config.Config;
import de.rj.urwalking.config.Constants;
import de.rj.urwalking.data.Experiment;
import de.rj.urwalking.data.survey.Question;
import de.rj.urwalking.data.survey.SalientObjectSurvey;
import de.rj.urwalking.db.DBExperiment;

/**
 * Singleton class that is used to save experiments as well as the loaded questions.
 * 
 * @author rjj
 * 
 */

public class ExperimentManager {

	public static final int SAVING_SUCCESS = 1;
	public static final int SAVING_ERROR = 2;
	public static final int SAVING_ABORTED = 3;

	private static ExperimentManager instance = new ExperimentManager();
	private HashMap<Integer, Question> questions = new HashMap<Integer, Question>();
	private ArrayList<DBExperiment> availableExperiments;

	/** Remembers which experiment was chosen in order to get the right questions. */
	private DBExperiment chosenExperiment;
	/** The current experiment. Is null when there is no experiment running. */
	private Experiment experiment;
	private boolean experimentRunning = false;
	private boolean isActiveVersion = false;

	public static ExperimentManager getInstance() {
		return instance;
	}

	/**
	 * Saves the loaded Questions.
	 * 
	 * @param questions
	 *            the Questions that were loaded from the database
	 */
	public void addQuestions(HashMap<Integer, Question> questions) {
		this.questions.putAll(questions);
	}

	public void setExperiments(ArrayList<DBExperiment> experiments) {
		availableExperiments = experiments;
	}

	/**
	 * Retrieves the previously loaded {@link DBExperiment}s for a given context (indoor/outdoor).
	 * 
	 * @param isOutdoor
	 *            true to get outdoor, false to get indoor experiments
	 * @return
	 */

	public ArrayList<DBExperiment> getAvailableExperiments(boolean isOutdoor) {

		ArrayList<DBExperiment> experiments = new ArrayList<DBExperiment>();

		for (DBExperiment e : availableExperiments) {
			if (e.isBlnoutdoor() == isOutdoor) {
				experiments.add(e);
			}
		}
		return experiments;

	}

	public void chooseExperiment(DBExperiment experiment) {
		chosenExperiment = experiment;
	}

	public void chooseTestmodeExperiment() {
		ArrayList<DBExperiment> experiments = getAvailableExperiments(true);
		for (DBExperiment exp : experiments) {
			if (exp.getUniqueid() == Config.TEST_EXPERIMENT_ID) {
				chooseExperiment(exp);
				break;
			}
		}
	}

	public SalientObjectSurvey getEmptySalientObjectSurvey() {

		// (deep) copy the Questions to get new, empty ones for each survey
		ArrayList<Question> copiedQuestions = new ArrayList<Question>();
		for (int id : chosenExperiment.getQuestionIds()) {
			Question q = (Question) DeepCopy.copy(questions.get(id));
			copiedQuestions.add(q);
		}
		Collections.sort(copiedQuestions);

		return new SalientObjectSurvey(copiedQuestions, experiment.getType());

	}

	public boolean isExperimentRunning() {
		return experimentRunning;
	}

	/**
	 * Starts a new experiment with the given parameters and tries to upload previous data.
	 * 
	 * @param participantId
	 * @param initialView
	 *            {@link Constants#CAMERA_MODE_OVERVIEW} or {@link Constants#CAMERA_MODE_STREETVIEW}
	 * @param allowViewChange
	 *            true if user can switch camera modes, false if not
	 * @param type
	 *            {@link Experiment#TYPE_INDOOR} or {@link Experiment#TYPE_OUTDOOR}
	 * @param urwv
	 *            the urwalking version
	 * @param devid
	 *            the unique device id
	 */

	public void newExperiment(int participantId, int initialView, boolean allowViewChange, int type, String urwv,
			String devid) {
		experiment = new Experiment(participantId, initialView, allowViewChange, type, Util.getAndroidVersion(),
				Util.getDeviceName(), chosenExperiment.getUniqueid(), urwv, devid);

		// set flag
		experimentRunning = true;

		// start FileUploadTask
		Log.i(Config.UPLOADTAG, "A new experiment was created; I therefore restart upload task.");
		FileUpload.getInstance().start();
	}

	/**
	 * Returns the current experiment.
	 */

	public Experiment getCurrentExperiment() {
		return experiment;
	}

	/**
	 * Ends the current experiment and saves the data to the database.
	 * 
	 * @param saveData
	 *            If this parameter is false, all experiment data will just be discarded (useful for test mode).
	 */

	public int finishExperiment(boolean saveData) {

		if (saveData) {
			boolean savingSuccessful = experiment.saveResultsXML(true, -1000);

			if (savingSuccessful) {
				experiment = null;
				experimentRunning = false;
				if (Config.DEBUG) Log.i(Config.UPLOADTAG, "Saving experiment successfully; I try to upload files.");
				FileUpload.getInstance().finish();
				return SAVING_SUCCESS;
			} else {
				return SAVING_ERROR;
			}
		} else {
			if (Config.DEBUG) Log.i(Config.UPLOADTAG, "Discarding experiment data as set in preferences.");
			experiment = null;
			experimentRunning = false;
			return SAVING_ABORTED;
		}
	}

	/**
	 * Discards the current experiment and doesn't save anything.
	 */
	public void finishExperimentWithoutSaving() {

		experiment = null;
		experimentRunning = false;
	}

	public void setActiveVersion(boolean isActive) {
		isActiveVersion = isActive;
	}

	public boolean isActiveVersion() {
		return isActiveVersion;
	}

}
